package sintactica;

import java.util.ArrayList;
import java.util.HashMap;
import sintactica.Simbol.ClsSimbol;
import sintactica.Simbol.TipSimbol;
import sintactica.Tip.TipValue;

/**
 *
 * @author bufu
 */
public class TabelaSimbol {

    private ArrayList<Simbol> m_tblSimbol;
    private HashMap<Simbol, Tip> m_tblConstante;

    public TabelaSimbol() {
        m_tblSimbol = new ArrayList<>();
        m_tblConstante = new HashMap<>();
    }

    private void formatPrintHeader(String message, int nr) {
        if (message.length() > nr) {
            System.out.print(message.substring(0, nr));
        } else {
            System.out.print(message);
            Simbol.printSpace(nr - message.length());
        }
        System.out.print(" ");
    }

    public void printTabelaSimboluri() {
        formatPrintHeader("index", Simbol.L_INDEX);
        formatPrintHeader("nume", Simbol.L_NUME);
        formatPrintHeader("clasa", Simbol.L_CLASA);
        formatPrintHeader("tip", Simbol.L_TIP);
        formatPrintHeader("index_val", Simbol.L_INDEX_VAL);
        formatPrintHeader("adrel", Simbol.L_ADREL);
        formatPrintHeader("deplrel", Simbol.L_DEPLREP);
        formatPrintHeader("nivel", Simbol.L_NIVEL);
        formatPrintHeader("nr_par", Simbol.L_NR_PAR);
        formatPrintHeader("dim_var", Simbol.L_DIM_VAR);
        formatPrintHeader("adr_start", Simbol.L_ADR_START);
        formatPrintHeader("lista_par", Simbol.L_LISTA_PAR);
        formatPrintHeader("nr_lista_par", Simbol.L_NR_LISTA_PAR);
        formatPrintHeader("ind_min", Simbol.L_IND_MIN);
        formatPrintHeader("ind_max", Simbol.L_IND_MAX);
        formatPrintHeader("lista_rec", Simbol.L_LISTA_REC);
        formatPrintHeader("nr_lista_rec", Simbol.L_NR_LISTA_REC);
        formatPrintHeader("incdom", Simbol.L_INCDOM);
        System.out.println("");
        for (int i = 0; i < m_tblSimbol.size(); i++) {
            System.out.println(m_tblSimbol.get(i).makeForPrint(i));
        }
    }

    public Simbol getSimbol(int index) {
        return m_tblSimbol.get(index);
    }

    public void addSimbol(Simbol simbol) {
        m_tblSimbol.add(simbol);
    }

    public void addConstanta(Simbol simbol, Tip valoare) {
        addSimbol(simbol);
        m_tblConstante.put(simbol, valoare);
    }

    public Tip getConstVal(Simbol simbol) {
        return m_tblConstante.get(simbol);
    }

    public int getIndex(Simbol s) {
        int index = m_tblSimbol.indexOf(s);
        if (index == -1) {
            System.err.println("cannot find simbol:" + s.nume);
        }
        return index;
    }

    public int getIndex(String nume) {
        for (int i = m_tblSimbol.size() - 1; i >= 0; i--) {
            if (m_tblSimbol.get(i).nume.equals(nume)) {
                return i;
            }
        }
        System.err.println("cannot find name:" + nume);
        return -1;
    }

    public void clearLevel(int nivel) {
        for (int i = m_tblSimbol.size() - 1; i >= 0; i--) {
            if (m_tblSimbol.get(i).nivel == nivel) {
                m_tblSimbol.remove(i);
            }
        }
    }

    public boolean isLastFunction(Simbol simbol) {
        Simbol current;
        for (int i = m_tblSimbol.size() - 1; i >= 0; i--) {
            current = m_tblSimbol.get(i);
            if (current.clasa == ClsSimbol.FUNCTIE || current.clasa == ClsSimbol.PROCEDURA) {
                return current == simbol;
            }
        }
        return false;
    }

    public Tip isMemberOf(String member, String struct) {
        Simbol sStruct = null, current, sPara = null;
        int index = -1;
        for (int i = m_tblSimbol.size() - 1; i >= 0; i--) {
            current = m_tblSimbol.get(i);
            if (current.nume.equals(struct)) {
                sStruct = current;
                index = i;
                break;
            }
        }
        if (sStruct == null) {
            return null;
        }
        if (sStruct.clasa != ClsSimbol.STRUCT) {
            return null;
        }
        for (int i = m_tblSimbol.size() - 1; i >= 0; i--) {
            current = m_tblSimbol.get(i);
            if (current.nume.equals(member)) {
                sPara = current;
                break;
            }
        }
        if (sPara == null) {
            return null;
        }
        if (sPara.clasa != ClsSimbol.CAMP_STRUCT) {
            return null;
        }
        if (isMemberOf(sPara, index)) {
            Tip tMember = new Tip();
            tMember.tip = sPara.tip;
            return tMember;
        } else {
            return null;
        }
    }

    private boolean isMemberOf(Simbol sComp, int indexSimbol) {
        ArrayList<Integer> lista = sComp.lista_rec;
        return lista.contains(indexSimbol);
    }

    public Tip hasConstanta(String nume) {
        Tip tConstanta;
        Simbol current;
        for (int i = m_tblSimbol.size() - 1; i >= 0; i--) {
            current = m_tblSimbol.get(i);
            if (current.clasa == ClsSimbol.CONSTANTA && current.nume.equals(nume)) {
                tConstanta = new Tip();
                tConstanta.tip = current.tip;
                return tConstanta;
            }
        }
        return null;
    }

    public Tip checkSubprog(String nume, ArrayList<Tip> lista) {
        Simbol current;
        for (int i = m_tblSimbol.size() - 1; i >= 0; i--) {
            current = m_tblSimbol.get(i);
            if (current.nume.equals(nume)
                    && (current.clasa == ClsSimbol.FUNCTIE
                    || current.clasa == ClsSimbol.PROCEDURA)) {
                if (current.lista_par.size() != lista.size()) {
                    return null;
                }
                for (int j = 0; j < lista.size(); j++) {
                    if (!compatibilParam(m_tblSimbol.get(current.lista_par.get(j)), lista.get(j))) {
                        return null;
                    }
                }
                Tip tSub = new Tip();
                tSub.tip = current.tip;
                tSub.lvalue = TipValue.ADRESA;
                return tSub;
            }
        }
        return null;
    }

    private boolean compatibilParam(Simbol param, Tip propus) {
        if (propus.tip == param.tip) {
            return true;
        }
        if (propus.tip == TipSimbol.INT && param.tip == TipSimbol.REAL && param.clasa == ClsSimbol.CAMP_PARA_REF) {
            return true;
        }

        return false;
    }
}
